﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using TopShooter.Items;

namespace TopShooter
{
	public interface IProjectileOwner
	{
		void OnProjectile_EntityImpact(Projectile projectile, Entity unit);
		void OnProjectile_TileImpact(Projectile projectile, Tile tile, Coordinate coord);
		void OnProjectile_Expired(Projectile projectile, bool hitGround);
	}

	public class Projectile
	{
		public Vector2 Location;
		public Vector2 Velocity;
		static public float MaxMoveSpeed = 4000.0f;
		public IProjectileOwner Owner;

		public Vector2 TargetLocation;
		public float DistanceRemaining;
		public bool HasCollided;
		private float _timeRemaining;
		public Vector2 Direction;
		public float TargetBuffer;

		private Texture2D _texture;
		private Vector2 _origin;
		private SoundEffect _projectileSound;
		public readonly Color Color = Color.Gold;
		public Scene Scene;
		public float Speed;
		public Entity TargetEntity;

		public string TextureName;
		public string Name;

		public const int UpdateSubIntervals = 4;
		public float ElapsedFraction = 1f / UpdateSubIntervals;
		private float _rotation;
		public float MaxDuration = 3;

		private Projectile() { }

		public Projectile(string name, string textureName, float speed)
		{
			Name = name;
			TextureName = textureName;
			Speed = speed;
			TargetBuffer = 20;
		}
		public void AddToScene(Scene scene, IProjectileOwner owner, Vector2 loc, Entity targetEntity)
		{
			Scene = scene;
			Owner = owner;
			TargetEntity = targetEntity;
			Location = loc;
			Scene.Projectiles.Add(this);
			_timeRemaining = MaxDuration;
			_texture = scene.Content.Load<Texture2D>(TextureName);
			_origin = new Vector2(_texture.Width / 2.0f, _texture.Height / 2.0f);
		}

		private bool _DetermineVelocity()
		{
			TargetLocation = TargetEntity.Location;

			float dx = TargetLocation.X - Location.X;
			float dy = TargetLocation.Y - Location.Y;
			DistanceRemaining = (float)Math.Sqrt((dx * dx) + (dy * dy));

			if (DistanceRemaining <= TargetBuffer)
			{	// Has Collided
				Owner.OnProjectile_EntityImpact(this, TargetEntity);
				return true;
			}
			Direction = new Vector2(dx / DistanceRemaining, dy / DistanceRemaining);
			DistanceRemaining -= TargetBuffer;

			Velocity = Direction * Speed;
			_rotation = MathUtil.GetRotation(Velocity);
			return false;
		}

		public bool OnUpdate(float time)
		{
			float intervalTime = ElapsedFraction * time;
			if (_DetermineVelocity())
			{
			}

			for (int i = 0; i < UpdateSubIntervals; i++)
			{
				Location += intervalTime * Velocity;
				HandleCollisions();

				if (HasCollided)
					return true; // completed

				_timeRemaining -= intervalTime;
				if (_timeRemaining <= 0)
				{	// Reached Target!
					Owner.OnProjectile_Expired(this, false);
					return true;
				}
			}
			return false;
		}

		public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Vector2 offset)
		{
			spriteBatch.Draw(_texture, Location + offset, null, Color, _rotation, _origin, 1.0f, SpriteEffects.None, 0.0f);
		}

		private void HandleCollisions()
		{
			int col = (int)Math.Floor(Location.X / Tile.Width);
			int row = (int)Math.Floor(Location.Y / Tile.Height);

			TileCollision collision = Scene.GetCollision(col, row);
			if (collision == TileCollision.Blocked)
			{
				_timeRemaining = 0;
				Tile tile = Scene.GetTile(col, row);
				Owner.OnProjectile_TileImpact(this, tile, new Coordinate(col, row));
				//if (tile != null)
				//   tile.HandleDamage(col, row, Owner.Damage, Scene);
				return;
			}
			//else if (CollidesWithEntities)
			//{	// Not blocked, and collides with Entities
			//   ProcessEntityCollisions(Scene.Entities);
			//}
		}

		//private void ProcessEntityCollisions(IEnumerable<Entity> enities)
		//{
		//   foreach (Entity d in enities)
		//   {
		//      if (!d.IsDestructible)
		//         continue;

		//      Vector2 offset = d.Location - this.Location;
		//      if (offset.LengthSquared() <= d.BoundingRadius2)
		//      {	// It Collided
		//         float damage = (Owner.Damage + s_Randomizer.Next(1, 10));
		//         if (d.TakeDamage(damage, Owner))
		//         {
		//            _timeRemaining = 0;
		//            return;
		//         }
		//      }
		//   }
		//}

		public virtual Projectile Clone()
		{
			Projectile clone = this.MemberwiseClone() as Projectile;
			return clone;
		}
	}

}

